#include"net/tcp.hpp"

#undef  xuser
#define xuser   mixc::net_tcp::inc
#include"configure/limit.hpp"
#include"io/private/handler_info.hpp"
#include"macro/xdebug_fail.hpp"
#include<arpa/inet.h>
#include<errno.h>
#include<netinet/in.h>
#include<netinet/tcp.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>

namespace mixc::net_tcp::inc{
    using namespace ::mixc::net_tcp::origin::tcp;

    // static ip_port_pair_v4 get_ip_port_pair_v4(ixx fd, auto c_func){
    //     auto addr           = sockaddr_in{};
    //     auto len_addr       = socklen_t(sizeof(addr));
    //     c_func(int(fd), (sockaddr *)& addr, & len_addr);
    //     auto ip             = u32(ntohl(addr.sin_addr.s_addr));
    //     auto port           = ntohs(addr.sin_port);
    //     return ip_port_pair_v4{}.ip(ip).port(port);
    // }

    // extern ip_port_pair_v4 get_this_ip_port_pair_v4(ixx fd){
    //     return get_ip_port_pair_v4(fd, getsockname);
    // }

    // 不可靠，对端挂断时就获取不到该参数了，即使句柄还有效
    // extern ip_port_pair_v4 get_peer_ip_port_pair_v4(ixx fd){
    //     return get_ip_port_pair_v4(fd, getpeername);
    // }

    extern ip_port_pair_v4 get_this_ip_port_pair_v4(ixx fd){
        if (fd < 0 or fd >= inc::limit::max_handler){
            return {};
        }
        else{
            auto info       = inc::handler_info{}.load(fd);
            auto result     = ip_port_pair_v4{};
            result->port->from_local(info.sock.port);
            result->ip->from_local(info.sock.ip);
            return result;
        }
    }

    extern ip_port_pair_v4 get_peer_ip_port_pair_v4(ixx fd){
        return get_this_ip_port_pair_v4(fd);
    }

    uxx read_helper(ixx fd, voidp buffer, uxx bytes, i32 options){
        if (fd <= 0){
            return 0;
        }

        auto real_bytes     = ::recv(fd, buffer, bytes, options);
        return real_bytes < 0 ? 0 : uxx(real_bytes);
    }

    uxx write_helper(ixx fd, void const * buffer, uxx bytes, i32 options){
        if (fd <= 0){
            return 0;
        }

        auto real_bytes     = ::send(fd, buffer, bytes, options);
        auto error          = 0;
        auto size           = socklen_t(sizeof(error));

        // errno 对于 ::send(...MSG_NOSIGNAL) 不管用
        ::getsockopt(fd, SOL_SOCKET, SO_ERROR, & error, & size);

        if (error == EPIPE){
            return 0;
        }
        return real_bytes < 0 ? 0 : uxx(real_bytes);
    }

    bstate_t client_v4_base::open(reuse_address_t mode) const {
        return inc::base_net::open(sock_type_t::ipv4_stream, mode);
    }

    bstate_t client_v4_base::open_with_bind(ipv4 ip, u16 port, reuse_address_t mode) const {
        return inc::base_net::open_with_bind(sock_type_t::ipv4_stream, ip, port, mode);
    }

    bstate_t client_v4_base::bind(ipv4 ip, u16 port) const {
        return inc::base_net::bind(ip, port);
    }

    bstate_t client_v4_base::connect(ipv4 ip, u16 port) const {
        auto service        = sockaddr_in{
            .sin_family = AF_INET,
            .sin_port = inc::port{}.from_local(port)->net_form,
            .sin_addr = { ip->net_form },
            .sin_zero = {}
        };

        auto result         = ::connect(m_fd, (sockaddr *)& service, sizeof(service));

        if (result < 0){
            return bstate_t::fail;
        }
        else{
            return bstate_t::success;
        }
    }

    uxx client_v4_base::read(voidp buffer, uxx bytes) const {
        return read_helper(m_fd, buffer, bytes, MSG_NOSIGNAL);
    }

    uxx client_v4_base::read_non_block(voidp buffer, uxx bytes) const {
        return read_helper(m_fd, buffer, bytes, MSG_NOSIGNAL | MSG_DONTWAIT);
    }

    uxx client_v4_base::write(void const * buffer, uxx bytes) const {
        return write_helper(m_fd, buffer, bytes, MSG_NOSIGNAL);
    }

    uxx client_v4_base::write_non_block(void const * buffer, uxx bytes) const {
        return write_helper(m_fd, buffer, bytes, MSG_NOSIGNAL | MSG_DONTWAIT);
    }
}

namespace mixc::net_tcp::origin::tcp{
    bstate_t service_v4::open(tcp::reuse_address_t mode) const {
        return base_net::open(sock_type_t::ipv4_stream, mode);
    }

    bstate_t service_v4::bind(ipv4 ip, u16 port) const {
        return base_net::bind(ip, port);
    }

    bstate_t service_v4::open_with_bind(ipv4 ip, u16 port, tcp::reuse_address_t mode) const {
        return base_net::open_with_bind(sock_type_t::ipv4_stream, ip, port, mode);
    }

    bstate_t service_v4::listen(uxx max_link) const {
        return ::listen(m_fd, max_link) < 0 ? bstate_t::fail : bstate_t::success;
    }

    client_item_v4 service_v4::accept() const {
        auto addr           = sockaddr_in{};
        auto bytes          = u32(sizeof(sockaddr_in));
        auto fd             = ::accept(m_fd, (sockaddr *)& addr, & bytes);
        auto client         = client_item_v4{};

        if (client.m_fd = fd; fd < 0){
            return client;
        }

        xdebug_fail(fd >= inc::limit::max_handler){
            return client;
        }
        else{
            auto new_info   = inc::handler_info{};
            auto & sock     = new_info.sock;
            sock.ip         = inc::ipv4{}.from_net(addr.sin_addr.s_addr)->local_form;
            sock.port       = inc::port{}.from_net(addr.sin_port)->local_form;
            new_info.store(fd);
            return client;
        }
    }
}
